// 文件扫描模块
// 负责递归扫描网络共享文件夹，生成文件索引

const fs = require('fs').promises;
const path = require('path');
const config = require('../config/config');

class FileScanner {
  constructor() {
    this.scannedFiles = [];      // 扫描到的文件列表
    this.scannedFolders = [];    // 扫描到的文件夹列表
    this.scanProgress = {        // 扫描进度信息
      totalItems: 0,
      scannedItems: 0,
      currentPath: '',
      startTime: null,
      errors: []
    };
    this.shouldStop = false;     // 停止扫描标记
  }

  /**
   * 开始扫描指定目录
   * @param {string} targetPath - 要扫描的目录路径
   * @returns {Promise<Object>} 扫描结果
   */
  async startScan(targetPath = config.scan.targetPath) {
    console.log(`开始扫描目录: ${targetPath}`);
    
    // 初始化扫描状态
    this.resetScanState();
    this.scanProgress.startTime = new Date();
    this.scanProgress.currentPath = targetPath;

    try {
      // 检查目录是否存在和可访问
      await this.validatePath(targetPath);
      
      // 递归扫描目录
      await this.scanDirectory(targetPath);
      
      // 生成扫描结果
      const result = this.generateScanResult();
      
      console.log(`扫描完成！总文件数: ${result.totalFiles}, 总文件夹数: ${result.totalFolders}`);
      return result;
      
    } catch (error) {
      console.error('扫描过程中发生错误:', error);
      this.scanProgress.errors.push({
        path: targetPath,
        error: error.message,
        timestamp: new Date()
      });
      throw error;
    }
  }

  /**
   * 验证路径是否有效和可访问
   * @param {string} dirPath - 要验证的路径
   */
  async validatePath(dirPath) {
    try {
      const stats = await fs.stat(dirPath);
      if (!stats.isDirectory()) {
        throw new Error(`路径不是一个目录: ${dirPath}`);
      }
      
      // 测试读取权限
      await fs.access(dirPath, fs.constants.R_OK);
      console.log('路径验证成功，开始扫描...');
      
    } catch (error) {
      if (error.code === 'ENOENT') {
        throw new Error(`目录不存在或网络共享未挂载: ${dirPath}`);
      } else if (error.code === 'EACCES') {
        throw new Error(`没有权限访问目录: ${dirPath}`);
      } else if (error.code === 'ETIMEDOUT') {
        throw new Error(`网络连接超时，请检查网络状态: ${dirPath}`);
      }
      throw error;
    }
  }

  /**
   * 递归扫描目录
   * @param {string} dirPath - 当前扫描的目录路径
   * @param {string} relativePath - 相对路径（用于索引）
   */
  async scanDirectory(dirPath, relativePath = '') {
    // 检查是否需要停止扫描
    if (this.shouldStop) {
      console.log('扫描已被停止');
      return;
    }

    this.scanProgress.currentPath = dirPath;
    
    try {
      // 读取目录内容
      const items = await fs.readdir(dirPath, { withFileTypes: true });
      
      // 过滤排除的文件和文件夹
      const filteredItems = items.filter(item => 
        !this.shouldExclude(item.name)
      );

      // 分批处理项目，避免内存溢出
      for (let i = 0; i < filteredItems.length; i += config.performance.scanBatchSize) {
        const batch = filteredItems.slice(i, i + config.performance.scanBatchSize);
        await this.processBatch(batch, dirPath, relativePath);
        
        // 短暂暂停，避免CPU占用过高
        await this.delay(10);
      }

    } catch (error) {
      // 处理扫描错误，但不停止整个扫描过程
      await this.handleScanError(error, dirPath);
    }
  }

  /**
   * 处理一批文件/文件夹
   * @param {Array} batch - 要处理的项目批次
   * @param {string} currentDir - 当前目录路径
   * @param {string} relativePath - 相对路径
   */
  async processBatch(batch, currentDir, relativePath) {
    const promises = batch.map(async (item) => {
      const fullPath = path.join(currentDir, item.name);
      const itemRelativePath = path.join(relativePath, item.name);

      try {
        if (item.isDirectory()) {
          // 处理文件夹
          await this.processFolder(fullPath, itemRelativePath, item.name);
        } else if (item.isFile()) {
          // 处理文件
          await this.processFile(fullPath, itemRelativePath, item.name);
        }
        
        this.scanProgress.scannedItems++;
        
      } catch (error) {
        await this.handleScanError(error, fullPath);
      }
    });

    // 控制并发数量
    await this.limitConcurrency(promises, config.performance.maxConcurrentScans);
  }

  /**
   * 处理文件夹
   * @param {string} fullPath - 文件夹完整路径
   * @param {string} relativePath - 相对路径
   * @param {string} name - 文件夹名称
   */
  async processFolder(fullPath, relativePath, name) {
    const folderInfo = {
      name: name,
      path: relativePath,
      fullPath: fullPath,
      type: 'folder',
      created: null,
      modified: null,
      fileCount: 0,
      subfolderCount: 0
    };

    try {
      const stats = await fs.stat(fullPath);
      folderInfo.created = stats.birthtime;
      folderInfo.modified = stats.mtime;
    } catch (error) {
      console.warn(`获取文件夹统计信息失败: ${fullPath}`, error.message);
    }

    this.scannedFolders.push(folderInfo);
    
    // 递归扫描子目录
    await this.scanDirectory(fullPath, relativePath);
  }

  /**
   * 处理文件
   * @param {string} fullPath - 文件完整路径
   * @param {string} relativePath - 相对路径
   * @param {string} name - 文件名称
   */
  async processFile(fullPath, relativePath, name) {
    try {
      const stats = await fs.stat(fullPath);
      
      // 检查文件大小限制
      if (stats.size > config.scan.maxFileSize) {
        console.warn(`文件过大，跳过: ${fullPath} (${this.formatFileSize(stats.size)})`);
        return;
      }

      const fileInfo = {
        id: this.generateFileId(fullPath),
        name: name,
        path: relativePath,
        fullPath: fullPath,
        size: stats.size,
        sizeFormatted: this.formatFileSize(stats.size),
        extension: path.extname(name).toLowerCase(),
        type: this.getFileType(name),
        modified: stats.mtime,
        created: stats.birthtime,
        parent: path.dirname(relativePath)
      };

      this.scannedFiles.push(fileInfo);

    } catch (error) {
      console.warn(`处理文件时发生错误: ${fullPath}`, error.message);
    }
  }

  /**
   * 检查文件/文件夹是否应该被排除
   * @param {string} itemName - 项目名称
   * @returns {boolean} 是否应该排除
   */
  shouldExclude(itemName) {
    return config.scan.excludePatterns.some(pattern => {
      if (pattern.includes('*')) {
        // 处理通配符模式
        const regex = new RegExp(pattern.replace(/\*/g, '.*'));
        return regex.test(itemName);
      } else {
        // 精确匹配
        return itemName === pattern;
      }
    });
  }

  /**
   * 获取文件类型
   * @param {string} fileName - 文件名
   * @returns {string} 文件类型
   */
  getFileType(fileName) {
    const extension = path.extname(fileName).toLowerCase();
    
    for (const [type, extensions] of Object.entries(config.scan.fileTypes)) {
      if (extensions.includes(extension)) {
        return type;
      }
    }
    
    return 'other';
  }

  /**
   * 格式化文件大小
   * @param {number} bytes - 字节数
   * @returns {string} 格式化后的大小
   */
  formatFileSize(bytes) {
    if (bytes === 0) return '0 B';
    
    const k = 1024;
    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }

  /**
   * 生成文件唯一ID
   * @param {string} fullPath - 文件完整路径
   * @returns {string} 文件ID
   */
  generateFileId(fullPath) {
    // 使用路径的哈希值作为ID
    let hash = 0;
    for (let i = 0; i < fullPath.length; i++) {
      const char = fullPath.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash = hash & hash; // 转换为32位整数
    }
    return 'f' + Math.abs(hash).toString(36);
  }

  /**
   * 处理扫描错误
   * @param {Error} error - 错误对象
   * @param {string} itemPath - 出错的路径
   */
  async handleScanError(error, itemPath) {
    const errorInfo = {
      path: itemPath,
      error: error.message,
      code: error.code,
      timestamp: new Date()
    };
    
    this.scanProgress.errors.push(errorInfo);
    
    // 根据错误类型决定是否重试
    if (error.code === 'ETIMEDOUT' && this.scanProgress.errors.length < config.performance.maxRetries) {
      console.log(`网络超时，重试扫描: ${itemPath}`);
      await this.delay(5000); // 等待5秒后重试
      // 这里可以添加重试逻辑
    } else {
      console.warn(`跳过无法访问的项目: ${itemPath} - ${error.message}`);
    }
  }

  /**
   * 限制并发执行数量
   * @param {Array} promises - Promise数组
   * @param {number} limit - 并发限制
   */
  async limitConcurrency(promises, limit) {
    const results = [];
    for (let i = 0; i < promises.length; i += limit) {
      const batch = promises.slice(i, i + limit);
      const batchResults = await Promise.allSettled(batch);
      results.push(...batchResults);
    }
    return results;
  }

  /**
   * 延迟执行
   * @param {number} ms - 延迟毫秒数
   */
  delay(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * 生成扫描结果
   * @returns {Object} 扫描结果对象
   */
  generateScanResult() {
    const endTime = new Date();
    const duration = endTime - this.scanProgress.startTime;
    
    return {
      metadata: {
        lastUpdate: endTime.toISOString(),
        scanPath: config.scan.targetPath,
        totalFiles: this.scannedFiles.length,
        totalFolders: this.scannedFolders.length,
        scanDuration: this.formatDuration(duration),
        version: '1.0',
        errors: this.scanProgress.errors
      },
      files: this.scannedFiles,
      folders: this.scannedFolders,
      tree: this.buildDirectoryTree(),
      statistics: this.generateStatistics()
    };
  }

  /**
   * 构建目录树结构
   * @returns {Object} 目录树对象
   */
  buildDirectoryTree() {
    const tree = {};
    
    // 先构建文件夹结构
    this.scannedFolders.forEach(folder => {
      const pathParts = folder.path.split(path.sep).filter(part => part);
      let currentLevel = tree;
      
      pathParts.forEach((part, index) => {
        if (!currentLevel[part]) {
          currentLevel[part] = {
            type: 'folder',
            fileCount: 0,
            subfolderCount: 0,
            children: {}
          };
        }
        currentLevel = currentLevel[part].children;
      });
    });
    
    // 添加文件信息到树结构
    this.scannedFiles.forEach(file => {
      const pathParts = file.parent.split(path.sep).filter(part => part);
      let currentLevel = tree;
      
      // 导航到文件所在目录
      pathParts.forEach(part => {
        if (currentLevel[part]) {
          currentLevel = currentLevel[part].children;
        }
      });
      
      // 增加文件计数
      let countLevel = tree;
      pathParts.forEach(part => {
        if (countLevel[part]) {
          countLevel[part].fileCount++;
          countLevel = countLevel[part].children;
        }
      });
    });
    
    return tree;
  }

  /**
   * 生成统计信息
   * @returns {Object} 统计信息对象
   */
  generateStatistics() {
    const fileTypes = {};
    const largestFiles = [];
    
    // 统计文件类型
    this.scannedFiles.forEach(file => {
      fileTypes[file.type] = (fileTypes[file.type] || 0) + 1;
      largestFiles.push(file);
    });
    
    // 找出最大的文件
    largestFiles.sort((a, b) => b.size - a.size);
    const topLargestFiles = largestFiles.slice(0, 10).map(file => ({
      name: file.name,
      size: file.size,
      sizeFormatted: file.sizeFormatted,
      path: file.path
    }));
    
    return {
      fileTypes,
      largestFiles: topLargestFiles,
      totalSize: this.scannedFiles.reduce((sum, file) => sum + file.size, 0),
      averageFileSize: this.scannedFiles.length > 0 
        ? Math.round(this.scannedFiles.reduce((sum, file) => sum + file.size, 0) / this.scannedFiles.length)
        : 0
    };
  }

  /**
   * 格式化持续时间
   * @param {number} milliseconds - 毫秒数
   * @returns {string} 格式化的时间
   */
  formatDuration(milliseconds) {
    const seconds = Math.floor(milliseconds / 1000);
    const minutes = Math.floor(seconds / 60);
    const hours = Math.floor(minutes / 60);
    
    if (hours > 0) {
      return `${hours}:${(minutes % 60).toString().padStart(2, '0')}:${(seconds % 60).toString().padStart(2, '0')}`;
    } else if (minutes > 0) {
      return `${minutes}:${(seconds % 60).toString().padStart(2, '0')}`;
    } else {
      return `0:${seconds.toString().padStart(2, '0')}`;
    }
  }

  /**
   * 重置扫描状态
   */
  resetScanState() {
    this.scannedFiles = [];
    this.scannedFolders = [];
    this.scanProgress = {
      totalItems: 0,
      scannedItems: 0,
      currentPath: '',
      startTime: null,
      errors: []
    };
    this.shouldStop = false;
  }

  /**
   * 停止扫描
   */
  stopScan() {
    this.shouldStop = true;
    console.log('收到停止扫描请求');
  }

  /**
   * 强制重置所有扫描状态（解决状态不一致问题）
   */
  forceReset() {
    console.log('强制重置扫描器状态...');
    
    // 1. 停止扫描
    this.shouldStop = true;
    
    // 2. 重置所有状态
    this.resetScanState();
    
    // 3. 清理可能的定时器或异步操作
    // 这里可以根据需要添加更多清理逻辑
    
    console.log('扫描器状态强制重置完成');
  }

  /**
   * 获取扫描进度
   * @returns {Object} 当前扫描进度
   */
  getScanProgress() {
    return {
      ...this.scanProgress,
      isScanning: this.scanProgress.startTime !== null && !this.shouldStop,
      elapsedTime: this.scanProgress.startTime 
        ? new Date() - this.scanProgress.startTime 
        : 0
    };
  }
}

module.exports = FileScanner;